/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.explorer.propertysheet;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.beans.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ResourceBundle;
import java.util.Vector;
import javax.swing.*;
import javax.swing.event.*;
import org.openide.awt.SplittedPanel;
import org.openide.awt.Toolbar;
import org.openide.awt.ToolbarButton;
import org.openide.awt.ToolbarToggleButton;
import org.openide.awt.MouseUtils;
import org.openide.awt.JPopupMenuPlus;
import org.openide.util.datatransfer.PasteType;
import org.openide.NotifyDescriptor;
import org.openide.TopManager;
import org.openide.actions.*;
import org.openide.util.RequestProcessor;
import org.openide.util.Utilities;
import org.openide.util.actions.ActionPerformer;
import org.openide.util.actions.SystemAction;
import org.openide.util.NbBundle;
import org.openide.nodes.Node;
import org.openide.nodes.NodeAdapter;
import org.openide.actions.CopyAction;
import org.openide.actions.PasteAction;
/**
* Implements a "property sheet" for a set of selected beans.
*
* <P>
* <TABLE BORDER COLS=3 WIDTH=100%>
* <TR><TH WIDTH=15%>Property<TH WIDTH=15%>Property Type<TH>Description
* <TR><TD> <code>paintingStyle</code> <TD> <code>int</code> <TD> style of painting properties ({@link #ALWAYS_AS_STRING}, {@link #STRING_PREFERRED}, {@link #PAINTING_PREFERRED})
* <TR><TD> <code>currentPage</code> <TD> <code>int</code> <TD> currently showed page (e.g. properties, expert, events)
* <TR><TD> <code>expert</code> <TD> <code>boolean</code> <TD> expert mode as in the JavaBeans specifications
* </TABLE>
*
* @author Jan Jancura, Jaroslav Tulach
* @version 1.23, Sep 07, 1998
*/
public class PropertySheet extends JPanel {
/** generated Serialized Version UID */
static final long serialVersionUID = -7698351033045864945L;
// public constants ........................................................
/** Property giving current sorting mode. */
public static final String PROPERTY_SORTING_MODE = "sortingMode"; // NOI18N
/** Property giving current value color. */
public static final String PROPERTY_VALUE_COLOR = "valueColor"; // NOI18N
/** Property giving current disabled property color. */
public static final String PROPERTY_DISABLED_PROPERTY_COLOR = "disabledPropertyColor"; // NOI18N
/** Property with the current page index. */
public static final String PROPERTY_CURRENT_PAGE = "currentPage"; // NOI18N
/** Property for "plastic" mode. */ // NOI18N
public static final String PROPERTY_PLASTIC = "plastic"; // NOI18N
/** Property for the painting style. */
public static final String PROPERTY_PROPERTY_PAINTING_STYLE = "propertyPaintingStyle"; // NOI18N
/** Property for whether only writable properties should be displayed. */
public static final String PROPERTY_DISPLAY_WRITABLE_ONLY = "displayWritableOnly"; // NOI18N
/** Constant for showing properties as a string always. */
public static final int ALWAYS_AS_STRING = 1;
/** Constant for preferably showing properties as string. */
public static final int STRING_PREFERRED = 2;
/** Constant for preferably painting property values. */
public static final int PAINTING_PREFERRED = 3;
/** Constant for unsorted sorting mode. */
public static final int UNSORTED = 0;
/** Constant for by-name sorting mode. */
public static final int SORTED_BY_NAMES = 1;
/** Constant for by-type sorting mode. */
public static final int SORTED_BY_TYPES = 2;
/** Icon for the toolbar. */
static protected Icon iNoSort;
static protected Icon iAlphaSort;
static protected Icon iTypeSort;
static protected Icon iDisplayWritableOnly;
static protected Icon iCustomize;
/** "No properties" text. */ // NOI18N
private static String text;
/** Standart variable for localisation. */
static java.util.ResourceBundle bundle = NbBundle.getBundle (
PropertySheet.class
);
static {
iNoSort = new ImageIcon (PropertySheet.class.getResource ("/org/openide/resources/propertysheet/unsorted.gif")); // NOI18N
iAlphaSort = new ImageIcon (PropertySheet.class.getResource ("/org/openide/resources/propertysheet/sortedByNames.gif")); // NOI18N
iTypeSort = new ImageIcon (PropertySheet.class.getResource ("/org/openide/resources/propertysheet/sortedByTypes.gif")); // NOI18N
iDisplayWritableOnly = new ImageIcon (PropertySheet.class.getResource ("/org/openide/resources/propertysheet/showWritableOnly.gif")); // NOI18N
iCustomize = new ImageIcon (PropertySheet.class.getResource ("/org/openide/resources/propertysheet/customize.gif")); // NOI18N
text = getString ("CTL_NoProperties");
}
static String getString (String str) {
return bundle.getString (str);
}
/**
* Returns description of the property. This description is showen
* in the tool tip for example.
*/
static String getDescription (PropertyDetails propertyDetails) {
StringBuffer sb = new StringBuffer ();
String shortDescription = propertyDetails.getShortDescription ();
if (shortDescription == null) shortDescription = ""; // NOI18N
// Handle Swing 1.1.1 HTML tooltips.
if (shortDescription.startsWith ("<html>")) { // NOI18N
shortDescription = shortDescription.substring (6);
sb.append ("<html>"); // NOI18N
}
if (propertyDetails.getPropertyEditor () == null)
sb.append (getString ("CTL_NoPropertyEditor")).append (' ');
else {
// [PENDING] should be localized
try {
sb.append (propertyDetails.canRead () ? "(r/" : "(-/"); // NOI18N
} catch (Exception e) {
sb.append ("(?/"); // NOI18N
}
try {
sb.append (propertyDetails.canWrite () ? "w) " : "-) "); // NOI18N
} catch (Exception e) {
sb.append ("?) "); // NOI18N
}
}
sb.append (shortDescription);
return new String (sb);
}
/**
* Returns description of the property value. This description is showen
* in the tool tip for example.
*/
static String getValueDescription (PropertyDetails propertyDetails) {
return Utilities.getClassName (propertyDetails.getValueType ());
}
/** Comparator which compares types */
private final static java.util.Comparator SORTER_TYPE = new java.util.Comparator () {
public int compare (Object l, Object r) {
String s1 = ((PropertyDetails)l).getValueType().getName();
String s2 = ((PropertyDetails)r).getValueType().getName();
int s = s1.compareToIgnoreCase (s2);
if (s != 0) return s;
s1 = ((PropertyDetails)l).getName();
s2 = ((PropertyDetails)r).getName();
return s1.compareToIgnoreCase(s2);
}
};
/** Comparator which compares PropertyDeatils names */
private final static java.util.Comparator SORTER_NAME = new java.util.Comparator () {
public int compare (Object l, Object r) {
String s1 = ((PropertyDetails)l).getName();
String s2 = ((PropertyDetails)r).getName();
return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
}
};
// properties ...............................................................................
/** Style of showing property value (text x painting). */
private int propertyPaintingStyle = PropertySheetSettings.propertyPaintingStyle;
/** Is plastic property value. */
private boolean plastic = PropertySheetSettings.plastic;
/** When it's true only writable properties are showen. */
private boolean displayWritableOnly = PropertySheetSettings.displayWritableOnly;
private java.util.Comparator sorter = null;
private int sortingMode = PropertySheetSettings.sortingMode;
/** Foreground color of values. */
private Color valueColor = PropertySheetSettings.valueColor;
/** Foreground color of disabled properties. */
private Color disabledPropertyColor = PropertySheetSettings.disabledColor;
// private helper variables ..................................................................
/** Stores info. about currently selected beans */
private transient BeansDetails beansDetails;
/** Names of pages. */
private transient String[] tab;
/** Hints of pages. */
private transient String[] hints;
/** Stores objects for showing property values. */
private transient PropertyDisplayer[][] propertyDisplayer;
/** Stores info. about properties. */
private transient PropertyDetails[][] propertyDetails;
/** Values of all properties. */
private transient PropertyValue[][] propertyValue;
/** Currently selected page of prop. sheet. */
private int pageIndex = -1;
/** Lastly selected name of page of prop. sheet. */
private String selectedTabName = null;
/** Synch. lock. */
private transient Integer lock = new Integer (0);
private transient PropertyDisplayer lastSelectedLine;
private transient boolean ignorePropertyChanges = false;
// private variables for visual controls ...........................................
private transient JTabbedPane pages;
private transient EmptyPanel emptyPanel;
private transient NamesPanel[] namesPanel = new NamesPanel [0];
private transient NamesPanel[] valuesPanel = new NamesPanel [0];
private transient ToolbarToggleButton bNoSort, bAlphaSort, bTypeSort, bDisplayWritableOnly;
private transient ToolbarButton customizer;
private transient PropertyChangeListener settingsListener;
private transient BeansListener beansListener = new BeansListener ();
private transient ChangeListener tabListener =
new ChangeListener () {
public void stateChanged (ChangeEvent e) {
int index = pages.getSelectedIndex ();
setCurrentPage (index);
}
};
private transient JPopupMenu popupMenu;
{
popupMenu = new JPopupMenuPlus ();
// popupMenu.add (new CopyAction ().getPopupPresenter ());
// popupMenu.add (new PasteAction ().getPopupPresenter ());
// popupMenu.addSeparator ();
popupMenu.add (new SetDefaultValueAction ().getPopupPresenter ());
}
// init .............................................................................
/* When the view is deserialized, it is called before the initializeManager method.
* @see #initializeManager */
/** Create and initialize the property sheet.
* When the {@link PropertySheetView} is created, this is called from its constructor.
*/
public PropertySheet() {
propertyDetails = new PropertyDetails [0][];
propertyDisplayer = new PropertyDisplayer [0][];
tab = new String [0];
// visual problems ........................................
setLayout (new BorderLayout ());
pages = new JTabbedPane ();
emptyPanel = new EmptyPanel (text);
pages.setTabPlacement (JTabbedPane.BOTTOM);
add ("Center", emptyPanel); // NOI18N
// Toolbar
JPanel p = new JPanel (new FlowLayout (FlowLayout.LEFT, 0, 0));
p.add (bNoSort = new ToolbarToggleButton (iNoSort));
bNoSort.setToolTipText (getString ("CTL_NoSort"));
bNoSort.setSelected (true);
bNoSort.addActionListener (new ActionListener () {
public void actionPerformed (ActionEvent e) {
sortingMode = UNSORTED;
sorter = null;
bNoSort.setSelected (true);
bAlphaSort.setSelected (false);
bTypeSort.setSelected (false);
if (pageIndex != -1)
resort (pageIndex);
}
});
p.add (bAlphaSort = new ToolbarToggleButton (iAlphaSort));
bAlphaSort.setToolTipText (getString ("CTL_AlphaSort"));
bAlphaSort.addActionListener (new ActionListener () {
public void actionPerformed (ActionEvent e) {
sortingMode = SORTED_BY_NAMES;
sorter = SORTER_NAME;
bNoSort.setSelected (false);
bAlphaSort.setSelected (true);
bTypeSort.setSelected (false);
if (pageIndex != -1)
resort (pageIndex);
}
});
p.add (bTypeSort = new ToolbarToggleButton (iTypeSort));
bTypeSort.setToolTipText (getString ("CTL_TypeSort"));
bTypeSort.addActionListener (new ActionListener () {
public void actionPerformed (ActionEvent e) {
sortingMode = SORTED_BY_TYPES;
sorter = SORTER_TYPE;
bNoSort.setSelected (false);
bAlphaSort.setSelected (false);
bTypeSort.setSelected (true);
if (pageIndex != -1)
resort (pageIndex);
}
});
try {
setSortingMode (sortingMode);
} catch (PropertyVetoException e) {
try {
setSortingMode (UNSORTED);
} catch (PropertyVetoException ee) {
}
}
Toolbar.Separator ts = new Toolbar.Separator ();
p.add (ts);
ts.updateUI ();
bDisplayWritableOnly = new ToolbarToggleButton (
iDisplayWritableOnly,
displayWritableOnly
);
bDisplayWritableOnly.setToolTipText (getString ("CTL_VisibleWritableOnly"));
bDisplayWritableOnly.addItemListener (new ItemListener() {
public void itemStateChanged (ItemEvent e) {
setDisplayWritableOnly (bDisplayWritableOnly.isSelected ());
}
});
p.add(bDisplayWritableOnly);
ts = new Toolbar.Separator ();
p.add (ts);
ts.updateUI ();
p.add (customizer = new ToolbarButton (iCustomize));
customizer.setToolTipText (getString ("CTL_Customize"));
customizer.setEnabled (false);
customizer.addActionListener (new ActionListener () {
public void actionPerformed (ActionEvent e) {
invokeCustomization ();
}
});
add ("North", p); // NOI18N
// close input component on ESC
registerKeyboardAction (new ActionListener () {
public void actionPerformed (ActionEvent e) {
if (pageIndex >= 0) removeInputComponent (pageIndex);
}
}, KeyStroke.getKeyStroke (java.awt.event.KeyEvent.VK_ESCAPE, 0),
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
PropertySheetSettings.getDefault ().addPropertyChangeListener (settingsListener =
new PropertyChangeListener () {
public void propertyChange (PropertyChangeEvent e) {
String name = e.getPropertyName ();
if (name == null) return;
if (name.equals (PROPERTY_VALUE_COLOR)) {
setValueColor ((Color)e.getNewValue ());
} else
if (name.equals (PROPERTY_DISABLED_PROPERTY_COLOR)) {
setDisabledPropertyColor ((Color)e.getNewValue ());
} else
if (name.equals (PROPERTY_SORTING_MODE)) {
try {
setSortingMode (((Integer)e.getNewValue ()).intValue ());
} catch (PropertyVetoException ee) {
}
} else
if (name.equals (PROPERTY_PLASTIC)) {
setPlastic (((Boolean)e.getNewValue ()).booleanValue ());
} else
if (name.equals (PROPERTY_PROPERTY_PAINTING_STYLE)) {
setPropertyPaintingStyle (((Integer)e.getNewValue ()).intValue ());
} else
if (name.equals (PROPERTY_DISPLAY_WRITABLE_ONLY)) {
setDisplayWritableOnly (((Boolean)e.getNewValue ()).booleanValue ());
}
}
}
);
setNodes (new Node [0]);
}
// public methods ........................................................................
/**
* Set the nodes explored by this property sheet.
*
* @param node nodes to be explored
*/
public void setNodes (Node[] node) {
postSetNodes (node);
}
/**
* Set property paint mode.
* @param style one of {@link #ALWAYS_AS_STRING}, {@link #STRING_PREFERRED}, or {@link #PAINTING_PREFERRED}
*/
public void setPropertyPaintingStyle (int style) {
propertyPaintingStyle = style;
if (pageIndex != -1) resort (pageIndex);
}
/**
* Get property paint mode.
*
* @return the mode
* @see #setPropertyPaintingStyle
*/
public int getPropertyPaintingStyle () {
return propertyPaintingStyle;
}
/**
* Set the sorting mode.
*
* @param sortingMode one of {@link #UNSORTED}, {@link #SORTED_BY_NAMES}, {@link #SORTED_BY_TYPES}
*/
public void setSortingMode (int sortingMode) throws PropertyVetoException {
switch (sortingMode) {
case UNSORTED:
sorter = null;
break;
case SORTED_BY_NAMES:
sorter = SORTER_NAME;
break;
case SORTED_BY_TYPES:
sorter = SORTER_TYPE;
break;
default:
throw new PropertyVetoException (bundle.getString ("EXC_Unknown_sorting_mode"),
new PropertyChangeEvent (this, PROPERTY_SORTING_MODE,
new Integer (this.sortingMode), new Integer (sortingMode)));
}
this.sortingMode = sortingMode;
bNoSort.setSelected (sortingMode == UNSORTED);
bAlphaSort.setSelected (sortingMode == SORTED_BY_NAMES);
bTypeSort.setSelected (sortingMode == SORTED_BY_TYPES);
if (pageIndex != -1) resort (pageIndex);
}
/**
* Get the sorting mode.
*
* @return the mode
* @see #setSortingMode
*/
public int getSortingMode () {
return sortingMode;
}
/**
* Set the currently selected page.
*
* @param index index of the page to select
*/
public void setCurrentPage (int index) {
if (pageIndex == index) return;
if (pageIndex >= 0) removeInputComponent (pageIndex);
pageIndex = index;
if (index < 0) return;
if (propertyDetails [index] == null) refreshTab (index);
else updateTab (index);
if (index != pages.getSelectedIndex ()) pages.setSelectedIndex (index);
selectedTabName = pages.getTitleAt (index);
}
/**
* Set the currently selected page.
*
* @param str name of the tab to select
*/
public boolean setCurrentPage (String str) {
int index = pages.indexOfTab (str);
if (index < 0) return false;
setCurrentPage (index);
return true;
}
/**
* Get the currently selected page.
* @return index of currently selected page
*/
public int getCurrentPage () {
return pages.getSelectedIndex ();
}
/**
* Set whether buttons in sheet should be plastic.
* @param plastic true if so
* @see SheetButton#setPlastic
*/
public void setPlastic (boolean plastic) {
this.plastic = plastic;
if (pageIndex != -1) resort (pageIndex);
}
/**
* Test whether buttons in sheet are plastic.
* @return <code>true</code> if so
*/
public boolean getPlastic () {
return plastic;
}
/**
* Set the foreground color of values.
* @param color the new color
*/
public void setValueColor (Color color) {
this.valueColor = color;
if (pageIndex != -1) resort (pageIndex);
}
/**
* Get the foreground color of values.
* @return the color
*/
public Color getValueColor () {
return valueColor;
}
/**
* Set the foreground color of disabled properties.
* @param color the new color
*/
public void setDisabledPropertyColor (Color color) {
disabledPropertyColor = color;
if (pageIndex != -1) resort (pageIndex);
}
/**
* Get the foreground color of disabled properties.
* @return the color
*/
public Color getDisabledPropertyColor () {
return disabledPropertyColor;
}
/**
* Set whether only writable properties are displayed.
* @param b <code>true</code> if this is desired
*/
public void setDisplayWritableOnly (boolean b) {
if (displayWritableOnly == b) return;
displayWritableOnly = b;
if (pageIndex != -1) resort (pageIndex);
bDisplayWritableOnly.setSelected (displayWritableOnly);
}
/**
* Test whether only writable properties are currently displayed.
* @return <code>true</code> if so
*/
public boolean getDisplayWritableOnly () {
return displayWritableOnly;
}
// private helper methods ....................................................................
/**
* Refreshs propertysheet.
*/
private synchronized void refreshPropertySheet() {
pages.removeChangeListener (tabListener);
int i, k = pages.getTabCount ();
ignorePropertyChanges = true;
tab = beansDetails.getPropertySetDisplayNames ();
hints = beansDetails.getPropertySetHints ();
ignorePropertyChanges = false;
int l = tab.length;
int t = Math.min (k, l);
int tt = Math.max (k, l);
if (l != k) {
NamesPanel[] oldNames = namesPanel;
NamesPanel[] oldValues = valuesPanel;
namesPanel = new NamesPanel [l];
valuesPanel = new NamesPanel [l];
System.arraycopy (oldNames, 0, namesPanel, 0, t);
System.arraycopy (oldValues, 0, valuesPanel, 0, t);
}
propertyValue = new PropertyValue [l][];
propertyDisplayer = new PropertyDisplayer [l][];
propertyDetails = new PropertyDetails [l][];
for (i = 0; i < t; i++) {// old pages => rename
propertyValue [i] = null;
propertyDisplayer [i] = null;
propertyDetails [i] = null;
pages.setTitleAt (i, tab [i]);
}
for (i = k; i < l; i++) {// newly added pages
propertyValue [i] = null;
propertyDisplayer [i] = null;
propertyDetails [i] = null;
createTab (i);
}
for (i = k - 1; i >= l; i--) {// removed pages
pages.removeTabAt (i);
}
// empty? => add emptyPanel!
if (k != l)
if (l == 0) {
remove (pages);
add (emptyPanel, "Center"); // NOI18N
} else {
remove (emptyPanel);
add (pages, "Center"); // NOI18N
}
pageIndex = -1;
if (l > 0) {
if (!setCurrentPage (selectedTabName))
setCurrentPage (0);
}
pages.invalidate ();
invalidate ();
Component f = getParent ();
if (f != null) f.validate ();
else validate ();
pages.repaint ();
repaint ();
pages.addChangeListener (tabListener);
}
void resort (int index) {
int i, k = propertyDetails.length;
refreshTab (index);
pages.invalidate ();
Component f = getParent ();
if (f != null) f.validate ();
else validate ();
pages.repaint ();
}
/** Creates one tab in tabbed pane.
* Must be init.: tab[] (with values), hints [] (with values).
*/
private void createTab (int index) {
JPanel p = new JPanel ();
p.setLayout (new BorderLayout ());
p.add (new EmptyPanel (text), "Center"); // NOI18N
pages.addTab (tab [index], null, p, hints [index]);
}
/** Creates components in one tab.
* Must be init.: tab[] (with values), hints [] (with values),
* namesPanel[] & valuesPanel[] (only declared).
* Initializes: namesPanel [index] & valuesPanel [index] & creates visual rep. of one tab.
*/
private void createPane (int index, boolean empty) {
JPanel p = (JPanel) pages.getComponentAt (index);
Component c = p.getComponent (0);
if (empty) {
if ((c != null) && (c instanceof EmptyPanel))
return;
p.removeAll ();
p.add (new EmptyPanel (text), "Center"); // NOI18N
return;
}
if (namesPanel [index] == null) {
namesPanel [index] = new NamesPanel ();
valuesPanel [index] = new NamesPanel (namesPanel [index]);
} else {
namesPanel [index].removeAll ();
valuesPanel [index].removeAll ();
}
if ((c == null) || !(c instanceof JScrollPane)) {
p.removeAll ();
JScrollPane scrollPane = new JScrollPane ();
SplittedPanel splittedPanel = new ScrollableSplittedPanel (scrollPane, namesPanel [index]);
splittedPanel.add (namesPanel [index], SplittedPanel.ADD_LEFT);
splittedPanel.add (valuesPanel [index], SplittedPanel.ADD_RIGHT);
scrollPane.setViewportView (splittedPanel);
scrollPane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.getVerticalScrollBar ().setUnitIncrement (25);
p.add (scrollPane, "Center"); // NOI18N
}
}
/**
* Refreshs properties in one tab.
* Out: propertyValue [index][],
* propertyDisplayer [index][],
* propertyDetails [index][]
*
* @param index Index of tab to refresh.
*/
private void refreshTab (int index) {
SheetButton sheetButton;
ignorePropertyChanges = true;
propertyDetails [index] = beansDetails.getPropertyDetails (index);
int i, k = propertyDetails [index].length;
if (displayWritableOnly) {
Vector v = new Vector (k);
for (i = 0; i < k; i++)
if (propertyDetails [index][i].canWrite ()) v.addElement (propertyDetails [index][i]);
propertyDetails [index] = new PropertyDetails [k = v.size ()];
v.copyInto (propertyDetails [index]);
}
if (sorter != null) java.util.Arrays.sort (propertyDetails [index], sorter);
createPane (index, k == 0);
propertyValue [index] = new PropertyValue [k];
propertyDisplayer [index] = new PropertyDisplayer [k];
for (i = 0; i < k; i++) {
sheetButton = new SheetButton (
propertyDetails [index][i].getName (),
plastic,
plastic
);
final PropertyDetails pd = propertyDetails [index][i];
sheetButton.setFocusTransferable (true);
sheetButton.addMouseListener (
new MouseUtils.PopupMouseAdapter () {
public void showPopup (MouseEvent ev) {
if (MouseUtils.isRightMouseButton (ev)) {
keepFocus = true;
setActions (pd);
popupMenu.show ((java.awt.Component) ev.getSource (), ev.getX(), ev.getY ());
}
}
}
);
sheetButton.addFocusListener (new FocusListener () {
public void focusGained (FocusEvent fe) {
JComponent jc = (JComponent)fe.getComponent ();
jc.scrollRectToVisible (new Rectangle (jc.getSize ()));
}
public void focusLost (FocusEvent fe) {
removeActions (pd);
}
});
propertyValue [index][i] = new PropertyValue (propertyDetails [index][i]);
sheetButton.setToolTipText (getDescription (propertyDetails [index][i]));
propertyDisplayer [index][i] = new PropertyDisplayer (
propertyDetails [index][i],
propertyValue [index][i],
propertyPaintingStyle,
lock,
valueColor,
disabledPropertyColor,
plastic
);
propertyDisplayer [index][i].setToolTipText (
getValueDescription (propertyDetails [index][i])
);
propertyDisplayer [index][i].addPropertyChangeListener (beansListener);
SheetListener sheetListener = new SheetListener (
sheetButton, propertyDisplayer [index][i]);
sheetButton.addSheetButtonListener (sheetListener);
propertyDisplayer [index][i].addSheetButtonListener (sheetListener);
if (!displayWritableOnly) {
sheetButton.setEnabled (propertyDetails [index][i].canWrite ());
propertyDisplayer [index][i].setEnabled (propertyDetails [index][i].canEdit ());
}
sheetButton.setInactiveForeground (disabledPropertyColor);
namesPanel [index].add (sheetButton);
valuesPanel [index].add (propertyDisplayer [index][i]);
}
if (k > 0) {
namesPanel [index].repaint ();
valuesPanel [index].repaint ();
}
repaint ();
ignorePropertyChanges = false;
}
/**
* Updates values of the properties in one tab.
*
* @param index Index of tab to refresh.
*/
void updateTab (int index) {
ignorePropertyChanges = true;
int i, k = propertyDetails [index].length;
PropertyValue newValue;
for (i = 0; i < k; i++) {
if ((newValue = new PropertyValue (propertyDetails [index][i])).
equals (propertyValue [index][i]))
continue;
/*S ystem.out.println ("Property " +
propertyDetails [index][i].getName () +
" changes to: " + newValue);*/ // NOI18N
propertyValue [index][i] = newValue;
boolean editable = propertyDetails [index][i].canEdit ();
propertyDisplayer [index][i].setValue (newValue);
propertyDisplayer [index][i].setEnabled (editable);
propertyDisplayer [index][i].validate ();
}// for
ignorePropertyChanges = false;
}
/**
* Invokes the customization on the currently selected Node (JavaBean).
*/
void invokeCustomization () {
beansDetails.customize ();
}
/**
* Sets or removes input component for one property displayer.
*
* @param displayer Displayer for whis to set or remove the input component.
*/
void setInputComponent (PropertyDisplayer dis) {
if (lastSelectedLine == dis) {
long t = System.currentTimeMillis () - dis.getLastDeselectTime ();
if (t < 400) return;
}
boolean open = dis.getInputState (); // needed if focus losd dont work
removeInputComponent (pageIndex);
if (!open) {
dis.setInputState (true);
lastSelectedLine = dis;
}
}
/**
* Removes input component.
*/
void removeInputComponent (int index) {
if (lastSelectedLine != null) {
lastSelectedLine.setReadState ();
lastSelectedLine = null;
}
}
PasteType[] oldPaste;
CopyAction copy = new CopyAction ();
PasteAction paste = new PasteAction ();
ActionPerformer oldCopy;
boolean keepFocus;
SetDefaultValueAction setDefault = new SetDefaultValueAction ();
{
setDefault.setSurviveFocusChange (false);
}
void setActions (final PropertyDetails pd) {
// Enable / Disable PasteAction
/* PasteType[] pt = paste.getPasteTypes ();
if ( (pt != null) &&
((pt.length != 1) || !(pt [0].getClass ().equals (BeanPasteType.class)))
) {
oldPaste = pt;
}
Clipboard clipboard = TopManager.getDefault ().getClipboard ();
Transferable trans = clipboard.getContents (TopManager.getDefault ());
if (trans != null) {
DataFlavor[] flavor = trans.getTransferDataFlavors ();
int ii, kk = (flavor == null) ? 0 : flavor.length;
for (ii = 0; ii < kk; ii++)
if ((flavor [ii] instanceof TransferFlavors.BeanFlavor) &&
(Utilities.getObjectType (pd.getValueType ()).isAssignableFrom (
((TransferFlavors.BeanFlavor)flavor [ii]).getRepresentationClass ()))
) break;
if (ii != kk)
paste.setPasteTypes (new PasteType [] {
new BeanPasteType (pd, trans, flavor [ii])
});
else
paste.setPasteTypes (null);
} else
paste.setPasteTypes (null);
*/
// Enable / Disable DefalutValueAction
try {
if (pd.supportsDefaultValue () && pd.canWrite ())
setDefault.setActionPerformer (
new ActionPerformer () {
public void performAction (SystemAction a) {
pd.restoreDefaultValue ();
}
});
else
setDefault.setActionPerformer (null);
} catch (Exception e) {
setDefault.setActionPerformer (null);
}
// Enable / Disable CopyAction
/* ActionPerformer copyAp = new ActionPerformer () {
public void performAction (SystemAction a) {
Clipboard clip = TopManager.getDefault().getClipboard();
try {
TransferableOwner to = new BeanTransferableOwner (pd.getPropertyValue ());
clip.setContents (to, to);
} catch (Exception e) {
}
}
};
ActionPerformer ap = copy.getActionPerformer ();
if ((ap != null) && !(ap.getClass ().equals (copyAp.getClass ()))) {
oldCopy = ap;
}
copy.setActionPerformer (copyAp);
*/
}
void removeActions (PropertyDetails pd) {
if (keepFocus) {
keepFocus = false;
return;
}
paste.setPasteTypes (oldPaste);
oldPaste = null;
setDefault.setActionPerformer (null);
copy.setActionPerformer (oldCopy);
oldCopy = null;
}
public void addNotify () {
PropertySheetSettings.getDefault ().addPropertyChangeListener (
settingsListener
);
super.addNotify ();
}
public void removeNotify () {
if (beansDetails != null) {
beansDetails.removePropertyChangeListener (beansListener);
beansDetails.removeNodeListener (beansListener);
}
PropertySheetSettings.getDefault ().removePropertyChangeListener (
settingsListener
);
super.removeNotify ();
}
public Dimension getPreferredSize () {
return new Dimension (200, 300);
}
// innerclasses ..........................................................................
/**
* Listens on BeansDetails on property change & property set changes.
*/
private class BeansListener extends NodeAdapter {
/*
* This method is called when some property are changed. Then all properties are tested
* for changing of its value.
*/
public void propertyChange (PropertyChangeEvent evt) {
if (ignorePropertyChanges) return;
if (pageIndex != -1) {
final String n = evt.getPropertyName ();
final int thisPageIndex = pageIndex;
javax.swing.SwingUtilities.invokeLater ( new Runnable() {
public void run() {
if ((n != null) && n.equals (Node.PROP_PROPERTY_SETS)) {
beansDetails.refresh ();
refreshPropertySheet ();
} else {
updateTab (thisPageIndex);
}
}
} );
}
}
}
/**
* Scrollable enhancement of SplittedPanel.
*/
class ScrollableSplittedPanel extends SplittedPanel implements Scrollable {
static final long serialVersionUID = -623600999659692948L;
Component scroll;
Container element;
ScrollableSplittedPanel (Component scroll, Container element) {
this.scroll = scroll;
this.element = element;
}
/**
* Returns the preferred size of the viewport for a view component.
*
* @return The preferredSize of a JViewport whose view is this Scrollable.
*/
public Dimension getPreferredScrollableViewportSize () {
return super.getPreferredSize ();
}
/**
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left, greater than zero for down/right.
* @return The "unit" increment for scrolling in the specified direction
*/
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
Component[] c = element.getComponents ();
if (c.length < 1) return 1;
Dimension d = c [0].getSize ();
if (orientation == SwingConstants.VERTICAL) return d.height;
else return d.width;
}
/**
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left, greater than zero for down/right.
* @return The "block" increment for scrolling in the specified direction.
*/
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
if (orientation == SwingConstants.VERTICAL) return scroll.getSize ().height;
else return scroll.getSize ().width;
}
/**
* Return true if a viewport should always force the width of this
* Scrollable to match the width of the viewport.
*
* @return True if a viewport should force the Scrollables width to match its own.
*/
public boolean getScrollableTracksViewportWidth () {
return true;
}
/**
* Return true if a viewport should always force the height of this
* Scrollable to match the height of the viewport.
*
* @return True if a viewport should force the Scrollables height to match its own.
*/
public boolean getScrollableTracksViewportHeight () {
return false;
}
}
/**
* Creates connection between two butons in the same line in property sheet. It propagates
* buttonEntered and buttonExited events between them, and action clicked too.
*/
class SheetListener implements SheetButtonListener {
private SheetButton nameComponent;
private PropertyDisplayer valueComponent;
SheetListener (
SheetButton nameComponent,
PropertyDisplayer valueComponent
) {
this.nameComponent = nameComponent;
this.valueComponent = valueComponent;
}
public void sheetButtonEntered (ActionEvent e) {
if (e.getSource () == nameComponent) valueComponent.setPressed (true);
else nameComponent.setPressed (true);
}
public void sheetButtonExited (ActionEvent e) {
if (e.getSource () == nameComponent) valueComponent.setPressed (false);
else nameComponent.setPressed (false);
}
public void sheetButtonClicked (ActionEvent e) {
if ((e.getID () == ActionEvent.ACTION_FIRST) && valueComponent.rolling (true)) return;
if (e.getSource () == nameComponent) {
setInputComponent (valueComponent);
} else {
removeInputComponent (0);
lastSelectedLine = valueComponent;
if (!lastSelectedLine.getInputState ()) {
lastSelectedLine.setInputState (true);
}
}
}
}
/** Nodes instance of nodes */
private transient RunNodes runNodes;
/** Post the request for redraw of new nodes.
* @param nodes nodes to display in the sheet
*/
void postSetNodes (Node[] nodes) {
if (runNodes == null) {
runNodes = new RunNodes ();
}
runNodes.postNodes (nodes);
}
/** Request for introspection of nodes.
*/
class RunNodes implements Runnable {
/** Previous request with change of nodes.
*/
private RequestProcessor.Task runTask;
/** nodes to display */
private Node[] nodes;
public void postNodes (Node[] nodes) {
this.nodes = nodes;
if (runTask == null) {
runTask = RequestProcessor.createRequest (this);
}
runTask.schedule (250);
}
/** cycle for work with beans */
public void run () {
Node[] n = nodes;
if (n != null) {
synchronized (lock) {
try {
if (beansDetails != null) {
beansDetails.removePropertyChangeListener (beansListener);
beansDetails.removeNodeListener (beansListener);
}
try {
customizer.setEnabled (false);
beansDetails = new BeansDetails (n);
} catch (IntrospectionException e) {
// empty beans details
beansDetails = new BeansDetails ();
}
beansDetails.addPropertyChangeListener (beansListener);
beansDetails.addNodeListener (beansListener);
customizer.setEnabled (beansDetails.hasCustomizer ());
SwingUtilities.invokeLater (new Runnable () {
public void run () {
refreshPropertySheet ();
}
});
} catch (Throwable e) {
if (e instanceof ThreadDeath)
throw (ThreadDeath)e;
TopManager.getDefault().notifyException(e);
}
}
}
}
}
/**
* Supports adding of serialized JavaBeans.
*/
private class BeanPasteType extends PasteType {
PropertyDetails details;
Transferable transferable;
DataFlavor flavor;
/** Constructs new BeanPasteType for the specific type of operation paste.
*/
public BeanPasteType (PropertyDetails details, Transferable transferable, DataFlavor flavor) {
this.details = details;
this.transferable = transferable;
this.flavor = flavor;
}
/* @return Human presentable name of this paste type. */
public String getName() {
return PropertySheet.this.bundle.getString ("CTL_Paste");
}
/* @return help */
public org.openide.util.HelpCtx getHelpCtx() {
return new org.openide.util.HelpCtx (BeanPasteType.class);
}
/** Performs the paste action.
* @return Transferable which should be inserted into the clipboard after
* paste action. It can be null, which means that clipboard content
* should stay the same.
*/
public Transferable paste () throws IOException {
String data;
try {
Object o = transferable.getTransferData (flavor);
if (o instanceof String)
o = java.beans.Beans.instantiate (
TopManager.getDefault ().currentClassLoader (),
(String) o
);
details.setPropertyValue (o);
beansListener.propertyChange (
new PropertyChangeEvent (this, null, null, null)
);
}
catch (java.awt.datatransfer.UnsupportedFlavorException e) {
throw new InternalError();
}
catch (ClassCastException e) {
throw new IOException (e.getMessage ());
}
catch (ClassNotFoundException e) {
throw new IOException (e.getMessage ());
}
return null;
}
}
class BeanTransferableOwner implements Transferable {
/** our data */
private Object object;
private DataFlavor flavor;
/**
* @param action is a being processed action
* @param copy is a flag indicating what kind of action we are doing
* true if copy, false if cut.
*/
public BeanTransferableOwner (Object object) {
this.object = object;
// flavor = new TransferFlavors.BeanFlavor (object.getClass ());
}
/** Returns an array of DataFlavor objects indicating the flavors the data can be provided in. The array should be
* ordered according to preference for providing the data (from most richly descriptive to least descriptive).
* @return an array of data flavors in which this data can be transferred
*/
public DataFlavor[] getTransferDataFlavors () {
return new DataFlavor[] {flavor};
}
/** Returns whether or not the specified data flavor is supported for this object.
* @param flavor - the requested flavor for the data
* @return boolean indicating wjether or not the data flavor is supported
*/
public boolean isDataFlavorSupported (DataFlavor flavor) {
return false; // PENDING flavor instanceof TransferFlavors.BeanFlavor;
}
/**Returns an object which represents the data to be transferred. The class of the object returned is defined by the
* representation class of the flavor.
* @param flavor - the requested flavor for the data
* @exception IOException if the data is no longer available in the requested flavor.
* @exception UnsupportedFlavorException if the requested data flavor is not supported.
*/
public Object getTransferData (DataFlavor flavor) throws
UnsupportedFlavorException, IOException {
if (isDataFlavorSupported (flavor)) return object;
throw new UnsupportedFlavorException (flavor);
}
}
}
/*
* Log
* 33 Gandalf 1.32 3/11/00 Martin Ryzl menufix [by E.Adams,
* I.Formanek]
* 32 Gandalf 1.31 1/12/00 Ian Formanek NOI18N
* 31 Gandalf 1.30 1/10/00 Jan Jancura Problem with
* initializing properties.
* 30 Gandalf 1.29 1/4/00 Jan Jancura Refresh PS when Property
* set is changed.
* 29 Gandalf 1.28 12/10/99 Jan Jancura Null pointerexception
* 28 Gandalf 1.27 12/9/99 Jan Jancura PropertyPanel
* implementation + Bug 3961
* 27 Gandalf 1.26 11/23/99 Jaroslav Tulach Clears last selected
* line when deselected not to prevent objects from GarbageCollection
* 26 Gandalf 1.25 11/8/99 Jesse Glick Apparently during IDE
* shutdown it is possible for a shortDescription to be null (??).
* 25 Gandalf 1.24 11/8/99 Jesse Glick Swing 1.1.1 HTML
* tooltips are handled for property short descriptions. BUT the display
* is currently too buggy to be of much use.
* 24 Gandalf 1.23 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 23 Gandalf 1.22 9/29/99 Jan Jancura Bug 1659 - all non
* editable properties are now grayed in the propertysheet
* 22 Gandalf 1.21 9/23/99 Jaroslav Tulach Uses schedule on
* delaying of node change firing.
* 21 Gandalf 1.20 8/17/99 Ian Formanek Generated serial version
* UID
* 20 Gandalf 1.19 8/1/99 Jaroslav Tulach MainExplorer now listens
* to changes in root elements.
* 19 Gandalf 1.18 7/25/99 Ian Formanek cleaned icons creation
* 18 Gandalf 1.17 7/3/99 Ian Formanek Survives when
* Transferable.getTransferDataFlavors returns null
* 17 Gandalf 1.16 6/24/99 Jesse Glick Gosh-honest HelpID's.
* 16 Gandalf 1.15 6/9/99 Ian Formanek Fixed resources for
* package change
* 15 Gandalf 1.14 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 14 Gandalf 1.13 4/28/99 Jan Jancura Bug in sorting
* 13 Gandalf 1.12 4/27/99 Jesse Glick new HelpCtx () ->
* HelpCtx.DEFAULT_HELP.
* 12 Gandalf 1.11 4/8/99 Ian Formanek Changed Object.class ->
* getClass ()
* 11 Gandalf 1.10 4/2/99 Jaroslav Tulach Preferred size
* 10 Gandalf 1.9 3/20/99 Jesse Glick [JavaDoc]
* 9 Gandalf 1.8 3/20/99 Jesse Glick [JavaDoc]
* 8 Gandalf 1.7 3/6/99 David Simonek
* 7 Gandalf 1.6 3/4/99 Jaroslav Tulach QuickSorter removed
* 6 Gandalf 1.5 3/4/99 Jan Jancura Localization moved
* 5 Gandalf 1.4 2/25/99 Jaroslav Tulach Change of clipboard
* management
* 4 Gandalf 1.3 2/17/99 Ian Formanek Updated icons to point
* to the right package (under ide/resources)
* 3 Gandalf 1.2 1/6/99 David Simonek
* 2 Gandalf 1.1 1/6/99 Ian Formanek Reflecting changes in
* location of package "awt"
* 1 Gandalf 1.0 1/5/99 Ian Formanek
* $
* Beta Change History:
* 0 Tuborg 0.20 --/--/98 Jaroslav Tulach Added multithreaded support
* 0 Tuborg 0.23 --/--/98 Ales Novak Serializable
* 0 Tuborg 0.24 --/--/98 Jan Formanek serialization improved, now extends Object and delegates the component
* 0 Tuborg 0.24 --/--/98 Jan Formanek via getComponent
* 0 Tuborg 0.25 --/--/98 Jan Formanek extends ExplorerViewSupport
* 0 Tuborg 0.53 --/--/98 Jan Formanek some fix (hopefully okay) - in method propertyChange
*/